/**
 * Octopus, an XMMS2 client.
 * Copyright (C) 2014 Octopus Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

namespace Octopus {
	public class ActivePlaylist : Object {
		private Gee.List<ActivePlaylistEntry> _list;
		private int _playback_status;

		public string name { get; private set; }

		public signal void playlist_add (ActivePlaylistEntry e);
		public signal void playlist_insert (ActivePlaylistEntry e, int pos);
		public signal void playlist_repeat_all_on ();
		public signal void playlist_repeat_all_off ();
		public signal void playlist_repeat_one_on ();
		public signal void playlist_repeat_one_off ();

		public ActivePlaylist () {
			Object ();
		}

		construct {
			_list = new Gee.LinkedList<ActivePlaylistEntry> ();
		}

		public void load_playlist () {
			//playlist
			Global.get ().get_xmms_client ().get_xmms ().playlist_current_active().notifier_set (on_playlist_loaded);
			Global.get ().get_xmms_client ().get_xmms ().broadcast_playlist_loaded ().notifier_set (on_playlist_loaded);
			Global.get ().get_xmms_client ().get_xmms ().broadcast_playlist_changed ().notifier_set (on_playlist_changed);
			//current pos
			Global.get ().get_xmms_client ().get_xmms ().broadcast_playlist_current_pos ().notifier_set (on_playlist_position);
			Global.get ().get_xmms_client ().get_xmms ().broadcast_collection_changed().notifier_set(on_collection_changed);
			//status
			Global.get ().get_xmms_client ().get_xmms ().playback_status().notifier_set (on_playback_status);
			Global.get ().get_xmms_client ().get_xmms ().broadcast_playback_status ().notifier_set (on_playback_status);
			//config
			Global.get ().get_xmms_client ().get_xmms ().config_list_values ().notifier_set (on_configval_changed);
			Global.get ().get_xmms_client ().get_xmms ().broadcast_config_value_changed ().notifier_set (on_configval_changed);
		}

		private bool on_playlist_loaded(Xmms.Value val) {
			unowned string playlist_name;

			if (val.get_string(out playlist_name)) {
				name = playlist_name;
				//requests the ids of that playlist
				Global.get ().get_xmms_client ().get_xmms ().playlist_list_entries(playlist_name).notifier_set(on_playlist_list_entries);
				Global.get ().get_xmms_client ().get_xmms ().playlist_current_pos (playlist_name).notifier_set(on_playlist_position);
			}
			return true;
		}

		/*
		 * Refresh the whole playlist.
		 */
		private bool on_playlist_list_entries(Xmms.Value val) {
			list_clear ();

			unowned Xmms.ListIter list_iter;
			val.get_list_iter(out list_iter);

			for (list_iter.first(); list_iter.valid(); list_iter.next()) {
				unowned Xmms.Value entry;
				int id = 0;

				if (!(list_iter.entry(out entry) && entry.get_int(out id)))
					continue;

				add (id);
			}
			return true;
		}

		private void add (int id) {
				var media_info = new ActivePlaylistEntry ();
				Global.get ().get_xmms_client ().get_xmms ().medialib_get_info (id).notifier_set (media_info.on_medialib_info);
				//add empty object, fill data when notified
				list_add (media_info);
		}

		private void insert (int id, int pos) {
				var media_info = new ActivePlaylistEntry ();
				Global.get ().get_xmms_client ().get_xmms ().medialib_get_info (id).notifier_set (media_info.on_medialib_info);
				//add empty object, fill data when notified
				list_insert (media_info, pos);
		}

		private void remove (int pos) {
				list_remove (pos);
		}

		private void move(int pos, int npos) {
				list_move (pos, npos);
		}

		private void clear () {
				list_clear ();
		}

		private bool on_playlist_changed(Xmms.Value val) {
			unowned string playlist;
			int id, change, pos, npos;

			val.dict_entry_get_int ("type", out change);
			val.dict_entry_get_int ("position", out pos);
			val.dict_entry_get_int ("newposition", out npos);
			val.dict_entry_get_int ("id", out id);
			val.dict_entry_get_string ("name", out playlist);

			if (name != playlist) return true;

			switch (change) {
				case Xmms.PlaylistChange.ADD:
					add (id);
					break;
				case Xmms.PlaylistChange.INSERT:
					insert (id, pos);
					break;
				case Xmms.PlaylistChange.REMOVE:
					remove (pos);
					break;
				case Xmms.PlaylistChange.MOVE:
					move(pos, npos);
					break;
				case Xmms.PlaylistChange.UPDATE:
					// not really interesting
					break;
				case Xmms.PlaylistChange.CLEAR:
					clear ();
					break;
				//reload playlist
				case Xmms.PlaylistChange.SHUFFLE:
				case Xmms.PlaylistChange.SORT:
				default:
					Global.get ().get_xmms_client ().get_xmms ().playlist_current_active ().notifier_set (on_playlist_loaded);
					break;
			}
			return true;
		}

		private void list_clear () {
			foreach (ActivePlaylistEntry e in _list) {
				e.remove (); //signal to remove view-entry
			}
			_list.clear ();
		}

		private void list_add (ActivePlaylistEntry e) {
			_list.add (e);
			playlist_add (e);
		}

		private void list_insert (ActivePlaylistEntry e, int pos) {
			_list.insert (pos, e);
			playlist_insert (e, pos);
		}

		private void list_remove (int pos) {
			if (pos < 0) {
				GLib.warning ("warning, list_remove (pos < 0) pos: %i", pos);
			} else {
				_list.get (pos).remove (); //signal to remove view-entry
				_list.remove_at (pos);
			}
		}

		private void list_move (int pos, int npos) {
			ActivePlaylistEntry e = _list.get (pos);
			//delete old position and insert at new position
			list_remove (pos);
			list_insert (e, npos);
			e.medialib_info_changed ();
		}

		public void xmms_playlist_set_next (ActivePlaylistEntry e) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_set_next (_list.index_of (e));
			Global.get ().get_xmms_client ().get_xmms ().playback_tickle ();
			if ((_playback_status == Xmms.PlaybackStatus.STOP) || (_playback_status == Xmms.PlaybackStatus.PAUSE)) {
				Global.get ().get_xmms_client ().get_xmms ().playback_start ();
			}
		}

		public void xmms_playlist_remove_entry (ActivePlaylistEntry e) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_remove_entry (Xmms.ACTIVE_PLAYLIST, _list.index_of (e));
		}

		public void xmms_playlist_clear () {
			if ((_playback_status == Xmms.PlaybackStatus.PLAY) || (_playback_status == Xmms.PlaybackStatus.PAUSE)) {
				Global.get ().get_xmms_client ().get_xmms ().playback_stop ();
			}
			Global.get ().get_xmms_client ().get_xmms ().playlist_clear (Xmms.ACTIVE_PLAYLIST);
		}

		public void xmms_playlist_add (int id) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_add_id (Xmms.ACTIVE_PLAYLIST, id);
		}

		public void xmms_playlist_add_and_set_next (int id) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_add_id (Xmms.ACTIVE_PLAYLIST, id);
			Global.get ().get_xmms_client ().get_xmms ().playlist_set_next (_list.size);
			Global.get ().get_xmms_client ().get_xmms ().playback_tickle ();
			if ((_playback_status == Xmms.PlaybackStatus.STOP) || (_playback_status == Xmms.PlaybackStatus.PAUSE)) {
				Global.get ().get_xmms_client ().get_xmms ().playback_start ();
			}
		}

		public void xmms_playlist_move_entry (uint from, uint to) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_move_entry (Xmms.ACTIVE_PLAYLIST, from, to);
		}

		public void xmms_playlist_insert (int pos, int id) {
			Global.get ().get_xmms_client ().get_xmms ().playlist_insert_id (Xmms.ACTIVE_PLAYLIST, pos, id);
		}

		public void xmms_playlist_repeat_all_on () {
			Global.get ().get_xmms_client ().get_xmms ().config_set_value ("playlist.repeat_all", "%d".printf(1));
		}

		public void xmms_playlist_repeat_all_off () {
			Global.get ().get_xmms_client ().get_xmms ().config_set_value ("playlist.repeat_all", "%d".printf(0));
		}

		public void xmms_playlist_repeat_one_on () {
			Global.get ().get_xmms_client ().get_xmms ().config_set_value ("playlist.repeat_one", "%d".printf(1));
		}

		public void xmms_playlist_repeat_one_off () {
			Global.get ().get_xmms_client ().get_xmms ().config_set_value ("playlist.repeat_one", "%d".printf(0));
		}

		public void xmms_playlist_shuffle () {
			Global.get ().get_xmms_client ().get_xmms ().playlist_shuffle (Xmms.ACTIVE_PLAYLIST);
		}

		public void xmms_playlist_rename (string new_name) {
			Global.get ().get_xmms_client ().get_xmms ().coll_rename (name, new_name, Xmms.COLLECTION_NS_PLAYLISTS).notifier_set ((val) => {
				if (val.is_error ()) {
					unowned string error;
					if (val.get_error(out error)) {
						Global.get ().message ("error: %s".printf (error));
						GLib.warning ("error renaming active playlist: %s", error);
					}
				}
				return true;
			});
		}

		private bool on_playlist_position(Xmms.Value val) {
			int pos;

			if (val.is_type(Xmms.ValueType.DICT)) {
				unowned string name;
				if (!val.dict_entry_get_int("position", out pos))
					return true;
				if (!val.dict_entry_get_string("name", out name))
					return true;
				if (this.name == name) {
					playlist_position(pos);
				} else {
					playlist_position(-1);
				}
			} else {
				if (!val.get_int(out pos))
					return true;
				playlist_position(pos);
			}
			return true;
		}

		private void playlist_position (int pos) {
			foreach (ActivePlaylistEntry e in _list) {
				//trigger signal only if we need to
				if (e.current_pos) {
					e.current_pos = false;
				}
			}
			//2 cases when xmms can send value -1
			// * when the current playing song is at position 0 and gets removed
			// * when playlist is empty and gets shuffled
			if (!(pos < 0)) { //only do this when pos is not < 0
				_list.get(pos).current_pos = true;
			}
		}

		private bool on_collection_changed(Xmms.Value val) {
			unowned string name, newname, ns;
			int change;

			val.dict_entry_get_string("name", out name);
			val.dict_entry_get_string("namespace", out ns);
			val.dict_entry_get_int("type", out change);

			if (   (name == this.name)
			    && (ns == Xmms.COLLECTION_NS_PLAYLISTS)
			    && (change == Xmms.CollectionChanged.RENAME)) {
				if (val.dict_entry_get_string("newname", out newname)) {
					this.name = newname;
				}
			}
			return true;
		}

		private bool on_playback_status(Xmms.Value val) {
			val.get_int(out _playback_status);
			return true;
		}

		private bool on_configval_changed(Xmms.Value val) {
			val.dict_foreach(on_configval_changed_foreach);
			return true;
		}

		private void on_configval_changed_foreach(string key, Xmms.Value val) {
			unowned string cfg_value;
			if (val.get_string(out cfg_value)) {
				if (   (key == "playlist.repeat_all")
				    && (cfg_value == "1")) {
					playlist_repeat_all_on ();
				}
				if (   (key == "playlist.repeat_all")
				    && (cfg_value == "0")) {
					playlist_repeat_all_off ();
				}
				if (   (key == "playlist.repeat_one")
				    && (cfg_value == "1")) {
					playlist_repeat_one_on ();
				}
				if (   (key == "playlist.repeat_one")
				    && (cfg_value == "0")) {
					playlist_repeat_one_off ();
				}
			}
		}
	}
}
